package com.koron.order.maintain.service.impl;

import cn.hutool.json.JSONUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.koron.bean.query.OrderItem;
import com.koron.bean.query.PageQuery;
import com.koron.bean.system.org.OrgBean;
import com.koron.bean.system.staff.po.AppUserPo;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.OrgCacheUtils;
import com.koron.common.core.util.message.MessageUtil;
import com.koron.common.task.web.entity.CronTask;
import com.koron.common.task.web.service.XxlJobService;
import com.koron.order.common.bean.base.OrderType;
import com.koron.order.common.bean.po.*;
import com.koron.order.common.service.*;
import com.koron.order.common.tools.CommonCodeTools;
import com.koron.order.common.tools.OrderCodeEnum;
import com.koron.order.maintain.bean.convertor.MaintainConvertor;
import com.koron.order.maintain.bean.dto.MaintainDetailDto;
import com.koron.order.maintain.bean.dto.MaintainTwinDto;
import com.koron.order.maintain.bean.entity.*;
import com.koron.order.maintain.bean.query.MaintainQuery;
import com.koron.order.maintain.bean.vo.MaintainDetailVo;
import com.koron.order.maintain.bean.vo.MaintainVo;
import com.koron.order.maintain.mapper.MaintainMapper;
import com.koron.order.maintain.service.MaintainEquipmentService;
import com.koron.order.maintain.service.MaintainExecuteService;
import com.koron.order.maintain.service.MaintainReadyService;
import com.koron.order.maintain.service.MaintainService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * (Maintain)表服务实现类
 *
 * @author zhouj
 * @since 2021-09-13 17:42:14
 */
@Service("maintainService")
@Slf4j
public class MaintainServiceImpl implements MaintainService {

    @Autowired
    private MaintainReadyService maintainReadyService;

    @Autowired
    private MaintainEquipmentService maintainEquipmentService;

    @Autowired
    private OrderInstructionService orderInstructionService;

    @Autowired
    private MaintainConvertor maintainConvertor;

    @Autowired
    private MaintainExecuteService executeService;

    /*工单的执行信息*/
    @Autowired
    private OrderExecuteService orderExecuteService;

    /*工单的设备*/
    @Autowired
    private OrderEquipService orderEquipService;

    //作业单位
    @Autowired
    private OrderWorkDeptService workDeptService;

    /*工单的工作地点*/
    @Autowired
    OrderLocationService orderLocationService;

    @Autowired
    OrgCacheUtils orgCacheUtils;

    @Autowired
    private XxlJobService xxlJobService;

    @Autowired
    MessageUtil messageUtil;
    @Autowired
    HttpServletRequest httpServletRequest;

    @Override
    @TaskAnnotation("insert")
    public int insert(SessionFactory factory, MaintainPo maintain) {

        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        return mapper.insert(maintain);
    }

    @Override
    @TaskAnnotation("update")
    public int update(SessionFactory factory, MaintainPo maintain) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        return mapper.update(maintain);
    }

    @Override
    @TaskAnnotation("updateStatus")
    public boolean updateStatus(SessionFactory factory, String id, String status) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        MaintainPo maintainPo = new MaintainPo();
        maintainPo.setId(id);
        maintainPo.setStatus(status);
        return mapper.update(maintainPo) > 0;
    }

    @Override
    @TaskAnnotation("deleteById")
    public int deleteById(SessionFactory factory, String id) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        return mapper.deleteById(id);
    }

    @Override
    @TaskAnnotation("queryByPlanId")
    public List<MaintainVo> queryByPlanId(SessionFactory factory, String planId) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        MaintainQuery maintainQuery = new MaintainQuery();
        maintainQuery.setPlanId(planId);
        List<MaintainVo> maintainVos = mapper.queryList(maintainQuery);
        return maintainVos;
    }

    @Override
    @TaskAnnotation("queryById")
    public MaintainDetailVo queryById(SessionFactory factory, String id) {
        //维护id 查询详情
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        MaintainPo maintainPo = mapper.queryById(id);

        MaintainDetailVo maintainDetailVo = maintainConvertor.beanToMaintainDetailVo(maintainPo);

        List<MaintainReadyPo> maintainReadyPoList = maintainReadyService.queryByOrderId(factory, id);

        MaintainEquipmentPo maintainEquipmentPo = new MaintainEquipmentPo();
        maintainEquipmentPo.setMaintainId(id);
        List<MaintainEquipmentPo> maintainEquipmentPos = maintainEquipmentService.queryList(factory, maintainEquipmentPo);

        //工单的作业指导书
        List<OrderInstructionBean> orderInstructionBeans = orderInstructionService.queryByBizId(factory, id);

        //工单的设备
        List<OrderEquipPo> orderEquipPos = orderEquipService.queryByOrderId(factory, id);

        //工单的工作地点信息
        List<OrderLocationPo> orderLocationPos = orderLocationService.queryByOrderId(factory, id);

        maintainDetailVo.setMaintainReadyPoList(maintainReadyPoList);
        maintainDetailVo.setMaintainEquipmentPoList(maintainEquipmentPos);
        maintainDetailVo.setOrderInstructionBeanList(orderInstructionBeans);
        maintainDetailVo.setOrderEquipPoList(orderEquipPos);
        maintainDetailVo.setOrderLocationList(orderLocationPos);

        return maintainDetailVo;
    }

    @Override
    @TaskAnnotation("queryMaintainBeanById")
    public MaintainPo queryMaintainBeanById(SessionFactory factory, String id) {
        //维护id 查询详情
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        MaintainPo maintainPo = mapper.queryById(id);

        return maintainPo;
    }

    @Override
    @TaskAnnotation("queryList")
    public PageInfo queryList(SessionFactory factory, MaintainQuery query, PageQuery pageQuery) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        EamUser currUser = getCurrUser();
        if ("1".equalsIgnoreCase(currUser.getIsOutOrg())) {
            if(CollectionUtils.isEmpty(getCurrUserOrgIds())){
                query.setDeptWorkList(new ArrayList<String>(){{add("null");}});
            }else {
                query.setDeptWorkList(getCurrUserOrgIds());
            }
        } else {
            if(CollectionUtils.isEmpty(getCurrUserOrgIds())){
                query.setDeptManageList(new ArrayList<String>(){{add("null");}});
            }else {
                query.setDeptManageList(getCurrUserOrgIds());
            }
        }
        // 设备向上归集查询
        equipmentCollectSearch(query, mapper);

        // 设置工单负责人和工单跟踪负责人查看权限
        query.setDeptWorkUser(currUser.getAccount());
        query.setCurrAccount(ThreadLocalContext.get().getAccount());
        query.setDeptId(StringUtils.isEmpty(ThreadLocalContext.get().getOrgId())?"":ThreadLocalContext.get().getOrgId());

        List<OrderItem> orders = pageQuery.getOrders();
        List<OrderItem> tempOrders = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(orders)) {
            for (OrderItem order : orders) {
                log.info(order.getColumn());
                if (order.getColumn().equals("`order_equip_po_list`")) {
                    tempOrders.add(order);
                }
            }
        }
        orders.removeAll(tempOrders);
        pageQuery.setOrders(orders);
        PageHelper.startPage(pageQuery.getPage(), pageQuery.getPageSize(), pageQuery.getOrderBy());


        List<MaintainVo> vos = mapper.queryList(query);

        if (CollectionUtils.isNotEmpty(vos)) {

            //设置红绿灯
            vos.forEach(p -> {
                String trafficLight = CommonCodeTools.calcTrafficLight(p.getFactEndTime(), p.getStartTime(), p.getEndTime(), p.getStatus());
                p.setTrafficLight(trafficLight);
            });

            //----------------以下采用批量工单ids查询提升效率

            List<String> orderIds = vos.stream().map(MaintainVo::getId).collect(Collectors.toList());
            //1.1 查询工单的作业单位
            List<OrderWorkDeptPo> orderWorkDeptPos = workDeptService.queryByOrderIds(factory, orderIds);
            //设置进列表
            if (CollectionUtils.isNotEmpty(orderWorkDeptPos)) {
                Map<String, List<OrderWorkDeptPo>> cond = orderWorkDeptPos.stream().collect(Collectors.groupingBy(OrderWorkDeptPo::getOrderId));
                for (MaintainVo maintainVo : vos) {
                    maintainVo.setWorkDeptPoList(cond.getOrDefault(maintainVo.getId(), new ArrayList<>()));
                }
            }

            //1.2 查询工单的设备列表
            List<OrderEquipPo> orderEquipPos = orderEquipService.queryByOrderIds(factory, orderIds);
            //设置进列表
            if (CollectionUtils.isNotEmpty(orderEquipPos)) {
                Map<String, List<OrderEquipPo>> cond = orderEquipPos.stream().collect(Collectors.groupingBy(OrderEquipPo::getOrderId));
                for (MaintainVo maintainVo : vos) {
                    maintainVo.setOrderEquipPoList(cond.getOrDefault(maintainVo.getId(), new ArrayList<>()));
                }
            }

            // 如果选了需要作业表单则查询出作业表单中的设备信息
            vos.stream().filter(maintainVo -> maintainVo.getNeedStatus().equals(1)).forEach(
                    maintainVo -> {
                        MaintainEquipmentPo maintainEquipmentPo = new MaintainEquipmentPo();
                        maintainEquipmentPo.setMaintainId(maintainVo.getId());
                        List<MaintainEquipmentPo> maintainEquipmentPos = maintainEquipmentService.queryList(factory, maintainEquipmentPo);
                        maintainVo.setMaintainEquipmentPos(maintainEquipmentPos);
                    }
            );
        }
        return new PageInfo(vos);
    }

    private void equipmentCollectSearch(MaintainQuery query, MaintainMapper mapper) {
        // 设备台站向上归集
        // 4-6 系统、设备、部件
        if (query.isCollectionSearch()){
            List<String> equipmentIdList = new ArrayList<>();
            List<Map<String,String>> result = new ArrayList<>();
            String parentEquipmentId = query.getEquipmentId();
            equipmentIdList.add(parentEquipmentId);
            recursionSearch(mapper,equipmentIdList,result);
            List<String> ids = result.stream()
                    .filter(map -> {
                        return Integer.valueOf(map.get("floor")) >= 4 && Integer.valueOf(map.get("floor")) <= 6;
                    }).map(e -> e.get("id"))
                    .distinct()
                    .collect(Collectors.toList());
            ids.add(parentEquipmentId);
            query.setEquipmentIds(ids);
            query.setEquipmentId(null);
        }
    }

    private void recursionSearch(MaintainMapper mapper, List<String> equipmentIdList, List<Map<String, String>> result) {
        List<Map<String,String>> equipment = mapper.getEquipmentByParentId(equipmentIdList);
        if (org.springframework.util.CollectionUtils.isEmpty(equipment)){
            return;
        }
        List<String> list = equipment.stream().map(e->e.get("id")).distinct().collect(Collectors.toList());
        result.addAll(equipment);
        recursionSearch(mapper,list,result);
    }

    // todo 后续优化批量查询
    public List<List<String>> batch(List<String> ids){
        int limit = ids.size();
        int batch = 50;
        List<List<String>> list = IntStream.iterate(0, i -> i + batch).limit((limit+batch-1)/batch)
                .mapToObj(start -> ids.stream().skip(start).limit(batch).collect(Collectors.toList())).collect(Collectors.toList());
        return list;
    }


    @Override
    @TaskAnnotation("submitTicketMaintain")
    public int submitTicketMaintain(SessionFactory factory, String maintainId, String status) {

        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        //对于工单id 及状态做处理
        MaintainPo maintainPo = queryMaintainBeanById(factory, maintainId);
        Assert.notNull(maintainPo, "工单id无效");
        String preStatus = maintainPo.getStatus();

        //将要改变的状态
        MaintainStatus maintainStatus = MaintainStatus.getMaintainStatus(status);
        Assert.notNull(maintainStatus, "状态修改,枚举不能为空");

        if (StringUtils.isNotEmpty(preStatus)) {
            MaintainStatus preMaintainStatus = MaintainStatus.getMaintainStatus(preStatus);
            Assert.notNull(preMaintainStatus, "数据有误");
            Assert.isTrue(maintainStatus.getSort() - preMaintainStatus.getSort() == 1, "不可重复提交");
        } else {
            Assert.isTrue(StringUtils.equalsAny(maintainStatus.getStatus(),
                    MaintainStatus.DRAFT.getStatus(), MaintainStatus.WAIT_DISPATCH.getStatus()), "修改工单状态失败");
        }
        MaintainPo po = new MaintainPo();
        po.setId(maintainId);
        po.setStatus(status);
        //状态变为待验收时-->设置为实际完成时间
        if (StringUtils.equals(status, MaintainStatus.WAIT_ACCEPT.getStatus())) {
            Date factEndTime = orderExecuteService.queryMaxFactEndTimeByOrderId(factory, maintainId);
            po.setFactEndTime(factEndTime);
        }

        return mapper.update(po);

    }

    @Override
    @TaskAnnotation("submitMaintain")
    public int submitMaintain(SessionFactory factory, String maintainId, String status, OrderDispatch dto) {

        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        //对于工单id 及状态做处理
        MaintainPo maintainPo = queryMaintainBeanById(factory, maintainId);
        Assert.notNull(maintainPo, "工单id无效");
        String preStatus = maintainPo.getStatus();

        //将要改变的状态
        MaintainStatus maintainStatus = MaintainStatus.getMaintainStatus(status);
        Assert.notNull(maintainStatus, "状态修改,枚举不能为空");

        if (StringUtils.isNotEmpty(preStatus)) {
            MaintainStatus preMaintainStatus = MaintainStatus.getMaintainStatus(preStatus);
            Assert.notNull(preMaintainStatus, "数据有误");
            Assert.isTrue(maintainStatus.getSort() - preMaintainStatus.getSort() == 1, "不可重复提交");
        } else {
            Assert.isTrue(StringUtils.equalsAny(maintainStatus.getStatus(),
                    MaintainStatus.DRAFT.getStatus(), MaintainStatus.WAIT_DISPATCH.getStatus()), "修改工单状态失败");
        }
        MaintainPo po = new MaintainPo();
        po.setId(maintainId);
        po.setStatus(status);
        //状态变为待验收时-->设置为实际完成时间
        if (StringUtils.equals(status, MaintainStatus.WAIT_ACCEPT.getStatus())) {
            Date factEndTime = orderExecuteService.queryMaxFactEndTimeByOrderId(factory, maintainId);
            po.setFactEndTime(factEndTime);
        }
        int result = mapper.update(po);
        // 维护工单发送派工通知
        if (StringUtils.equals(status, MaintainStatus.WAIT_EXECUTE.getStatus())) {
            if (com.koron.common.core.util.StringUtils.isNotEmpty(dto.getTopicWorker())) {
                String topicWorkerJson = dto.getTopicWorker();
                List<AppUserPo> userPoList = JSONUtil.parseArray(topicWorkerJson).toList(AppUserPo.class);
                String handler = userPoList.stream().map(AppUserPo::getAccount).collect(Collectors.joining(","));
                sendMessage(handler, dto, maintainPo);
            }
        }
        return result;
    }

    private void sendMessage(String handler, OrderDispatch orderDispatch, MaintainPo po) {
        String qywxHref = messageUtil.generateQywxHref("transmit_" + orderDispatch.getOrderType() + "_order", orderDispatch.getOrderId(),"","");
        // 如果工作内容为空则取注意事项
        messageUtil.sendMessageWithTemplate("dispatch_notice", qywxHref, handler, po.getContext());
    }

    @Override
    @TaskAnnotation("insertOrUpdate")
    public String insertOrUpdate(SessionFactory factory, MaintainDetailDto dto) {
        //基础信息
        MaintainPo maintainPo = maintainConvertor.dtoToBean(dto);

        maintainPo.setStatus(dto.getIsCommit() ? MaintainStatus.WAIT_DISPATCH.getStatus() : MaintainStatus.DRAFT.getStatus());

        String maintainId;

        Boolean updateFlag=true;

        //1.1 维护工单主表
        if (StringUtils.isEmpty(maintainPo.getId())) {
            maintainId = CodeTools.getCode32();
            maintainPo.setId(maintainId);
           // maintainPo.setCode(CommonCodeTools.getNormalOrderCode(OrderCodeEnum.WH));
            if (StringUtils.isEmpty(maintainPo.getCode())) {
                OrgBean orgBean = orgCacheUtils.getOrgById(maintainPo.getDeptManage());
                if(orgBean!=null){
                    String simpleEn = orgBean.getPrefix();
                    maintainPo.setCode(CommonCodeTools.getOrderCode(OrderCodeEnum.WH,simpleEn));
                }else {
                    maintainPo.setCode(CommonCodeTools.getNormalOrderCode(OrderCodeEnum.WH));
                }
            }

            insert(factory, maintainPo);
            updateFlag=false;
        } else {
            maintainId = maintainPo.getId();
            update(factory, maintainPo);
        }

        //1.2 工作地点
        List<OrderLocationPo> orderLocationList = dto.getOrderLocationList();
        String location = maintainPo.getLocation();
        String locationName = maintainPo.getLocationName();

        if (!StringUtils.isAllEmpty(location,locationName)) {
            OrderLocationPo orderLocationPo = new OrderLocationPo();
            orderLocationPo.setLocation(location);
            orderLocationPo.setLocationName(locationName);
            orderLocationList.add(orderLocationPo);
        }
        if (CollectionUtils.isNotEmpty(orderLocationList)) {
            orderLocationList.forEach(p -> {
                p.setOrderId(maintainId);
                p.setOrderType(OrderType.MAINTAIN);
            });
            orderLocationService.insertOrUpdateBatch(factory, orderLocationList);
        } else {
            if(updateFlag) orderLocationService.deleteByOrderId(factory, maintainId);
        }

        //1.3 作业准备
        List<MaintainReadyPo> maintainReadyPoList = dto.getMaintainReadyPoList();
        if (!CollectionUtils.isEmpty(maintainReadyPoList)) {
            maintainReadyPoList.forEach(p -> {
                p.setMaintainId(maintainId);
            });
            maintainReadyService.insertBatch(factory, maintainReadyPoList);
        }else {
            if(updateFlag) maintainReadyService.deleteByOrderId(factory,maintainId);
        }
        //1.4 作业过程
        List<MaintainEquipmentPo> maintainEquipmentPoList = dto.getMaintainEquipmentPoList();
        if (!CollectionUtils.isEmpty(maintainEquipmentPoList)) {
            maintainEquipmentPoList.forEach(p -> {
                p.setMaintainId(maintainId);
            });
            maintainEquipmentService.insertBatch(factory, maintainEquipmentPoList);
        }else {
            if(updateFlag) maintainEquipmentService.deleteByOrderId(factory,maintainId);
        }
        //1.5 作业指导书
        List<OrderInstructionBean> orderInstructionBeanList = dto.getOrderInstructionBeanList();
        if (!CollectionUtils.isEmpty(orderInstructionBeanList)) {
            orderInstructionBeanList.forEach(p -> {
                p.setBussinessId(maintainId);
                p.setOrderType(OrderType.MAINTAIN);
            });
            orderInstructionService.insertBatch(factory, orderInstructionBeanList);
        }else {
            if(updateFlag) orderInstructionService.deleteByBizId(factory, maintainId, OrderType.MAINTAIN);
        }

        //1.6 作业表单选择否时,保存设备
        List<OrderEquipPo> orderEquipPoList = dto.getOrderEquipPoList();

        if (!CollectionUtils.isEmpty(orderEquipPoList)) {
            orderEquipPoList.forEach(p -> {
                p.setOrderId(maintainId);
                p.setOrderType(OrderType.MAINTAIN);
            });
            orderEquipService.insertBatch(factory, orderEquipPoList);
        }else {
            if(updateFlag) orderEquipService.deleteByOrderId(factory,maintainId);
        }

        //生成项目及记录项记录
        executeService.generateStand(factory, maintainId);

        return maintainId;
    }

    @Override
    @TaskAnnotation("deprecated")
    public String deprecated(SessionFactory factory, String orderId) {

        //作废工单
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);

        MaintainPo maintainPo = new MaintainPo();
        maintainPo.setId(orderId);
        maintainPo.setStatus(MaintainStatus.DEPRECATED.getStatus());

        mapper.update(maintainPo);

        return "";
    }

    @Override
    @TaskAnnotation("queryTwin")
    public MaintainTwinDto queryTwin(SessionFactory factory, String eqId) {
        MaintainMapper mapper = factory.getMapper(MaintainMapper.class);
        MaintainTwinDto maintainTwinDto = null;
        try {
            List<MaintainPlanPo> planIdList = mapper.queryPlanIdByEqId(eqId);
            if (CollectionUtils.isEmpty(planIdList)) {
                return null;
            }
            maintainTwinDto = new MaintainTwinDto();
            MaintainTwinDto finalMaintainTwinDto = maintainTwinDto;
            planIdList.stream().forEach(maintainPlanPo -> {
                // 获取已经维保次数
                int count = mapper.queryMaintainTime(maintainPlanPo.getContent(), eqId);
                finalMaintainTwinDto.setEdqty(count);
                // 获取corn 表达式
                CronTask cronTask = xxlJobService.getCronTaskById(maintainPlanPo.getId());
                // 计算应该维保执行的次数
                finalMaintainTwinDto.setEqId(maintainPlanPo.getEquipmentId());
                finalMaintainTwinDto.setEqName(maintainPlanPo.getEquipmentName());
                Integer time = calToDoneTime(cronTask);
                finalMaintainTwinDto.setAllqty(Math.addExact(finalMaintainTwinDto.getAllqty(), time));
            });
            return maintainTwinDto;
        } catch (Exception e) {
            return null;
        }
    }

    private Integer calToDoneTime(CronTask cronTask) {
        if (ObjectUtils.isEmpty(cronTask)){
            return 0;
        }
        // 设置基础次数
        int baseTime = 1;
        String[] min = Optional.ofNullable(cronTask).map(CronTask::getMins).map(s -> s.split(",")).orElse(new String[1]);
        String[] hour = Optional.ofNullable(cronTask).map(CronTask::getHours).map(s -> s.split(",")).orElse(new String[1]);
        String[] day = Optional.ofNullable(cronTask).map(CronTask::getDays).map(s -> s.split(",")).orElse(new String[1]);
        String[] week = Optional.ofNullable(cronTask).map(CronTask::getWeeks).map(s -> s.split(",")).orElse(new String[0]);
        String[] month = Optional.ofNullable(cronTask).map(CronTask::getMonths).map(s -> s.split(",")).orElse(new String[1]);
        String[] year = Optional.ofNullable(cronTask).map(CronTask::getYears).map(s -> s.split(",")).orElse(new String[1]);
        // 维保计划定时任务corn表达式选择规则：如果选择了周则不能选择日。
        // 如果选择了周，则一个月默认执行4次
        if (week.length>0){
            return baseTime * 4 * month.length * year.length;
        }
        return min.length * hour.length * day.length * month.length * year.length;
    }

}
